package com.contentteam.util.config.metadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.apache.log4j.Logger;

import com.contentteam.util.config.GetterInterfaceBindingGuiceAbstractModul;
import com.contentteam.util.config.guice.Bind;

public class Getter {

	private static final Log LOG = new Log();

	private final Class<?> returnType;

	private final GetterInterfaceBindingGuiceAbstractModul<?> guiceModul;

	private final Method method;

	private final Class<? extends Annotation> annotatedWith;

	
	public Getter(final Method method,
			final Class<? extends Annotation> annotatedWith,
			final GetterInterfaceBindingGuiceAbstractModul<?> guiceModul) {

		this.returnType = method.getReturnType();

		this.guiceModul = guiceModul;

		this.method = method;
		
		this.annotatedWith = annotatedWith;
	}

	/**
	 * 
	 * @param method
	 * 
	 * @return Instance of a Getter, never NULL.
	 * 
	 * @throws InstantiationException
	 */
	public static Getter createInstance(final Method method,
			final GetterInterfaceBindingGuiceAbstractModul<?> guiceModul)
			throws CorruptGetterException {
		final Bind bind = method.getAnnotation(Bind.class);

		final boolean skipGetter = (bind == null);

		if (skipGetter) {
			final String logMsg = LOG.errHasNoBindAnnotation(method);

			throw new CorruptGetterException(method, logMsg);
		}

		final Class<? extends Annotation> annotatedWith = bind.value();

		return new Getter(method, annotatedWith, guiceModul);
	}

	
	
	public static boolean isAcceptableMethod(final Method method) {
		return hasBindAnnotation(method);
	}

	private static boolean hasBindAnnotation(final Method method) {
		final Bind bind = method.getAnnotation(Bind.class);

		return (bind != null);
	}

	private static final class Log {
		private static final Logger LOGGER = Logger.getLogger(Getter.class);

		public String errHasNoBindAnnotation(final Method method) {
			final String logMsg = "Couldnt instantiate Getter-delegate for method [= "
					+ MethodUtils.simplePrintMethodHeader(method)
					+ "] because no @Bind-annotations is used to mark this method. "
					+ "Use isAcceptableMethod(method) before you try to create an Instance of a Getter.";

			LOGGER.error(logMsg);

			return logMsg;
		}

		public String couldntInvokeMethod(
				final GetterInterfaceBindingGuiceAbstractModul<?> guiceModul,
				final Method method, Throwable e) {

			final String methodHeader = MethodUtils
					.simplePrintMethodHeader(method);

			final String logMsg = "Couldnt execute Getter/Method [="
					+ methodHeader + "]. Exception catched.";

			LOGGER.error(logMsg);

			return logMsg;
		}

	}

	public void invoke() {
		final Object result = invokeMethod();

		this.guiceModul.bindGetter(this.method, this.returnType, this.annotatedWith, result);
	}

	private Object invokeMethod() {
		try {
			return this.method.invoke(this.guiceModul);
		} catch (final Throwable e) {
			final String logMsg = LOG.couldntInvokeMethod(this.guiceModul,
					this.method, e);

			throw new RuntimeException(logMsg, e);
		}
	}

}
